package controllers

import (
	"fmt"
	"github.com/XieWeikai/PlaceToGo/models"
	"github.com/XieWeikai/PlaceToGo/utils"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
)

var requestKeyMap = map[string]string{
	"userId":             "user_id",
	"placeType":          "place_type",
	"requestTitle":       "title",
	"requestDescription": "description",
	"placeImage":         "image_url",
	"creationTime":       "created_at",
	"endTime":            "request_end_date",
	"maxPrice":           "max_price",
	"status":             "status",
}

func GetRequest(c *gin.Context) {
	var r models.Request
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		c.JSON(400, gin.H{"error": "invalid request id"})
		return
	}
	r.ID = uint(id)
	if _, err := r.GetFirst(); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "request with id not found"})
		return
	}
	c.JSON(200, &r)
}

func GetRequests(c *gin.Context) {
	filter, err := utils.ParseFilter(c.DefaultQuery("filter", "{}"))
	filter = utils.ReplaceKeys(filter, requestKeyMap)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	l, r, err := utils.ParseRange(c.DefaultQuery("range", "[0, 9]"))
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	sortBy, sortOrder, err := utils.ParseSort(c.DefaultQuery("sort", "[\"id\", \"asc\"]"))
	sortBy = requestKeyMap[sortBy]
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	requests, cnt, err := models.GetRequests(filter, int(r-l+1), int(l), sortBy, sortOrder)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	c.Header("Content-Range", fmt.Sprintf("requests %d-%d/%d", l, r, cnt))
	c.JSON(200, requests)
}

func CreateRequest(c *gin.Context) {
	var r models.Request
	uid := c.MustGet("user_id").(uint)
	if err := c.ShouldBindJSON(&r); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	r.UserID = uid
	if _, err := r.CreateRequest(); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	c.JSON(201, gin.H{"data": &r})
}

func UpdateRequest(c *gin.Context) {
	var r models.Request
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		c.JSON(400, gin.H{"error": "invalid request id"})
		return
	}
	r.ID = uint(id)
	if _, err := r.GetFirst(); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	uid := c.MustGet("user_id").(uint)
	if r.UserID != uid && c.MustGet("user_type").(string) != "admin" {
		c.JSON(http.StatusForbidden, gin.H{"error": "you are not allowed to update this request"})
		return
	}

	if r.Status != models.WaitingForResponse {
		c.JSON(400, gin.H{"error": "somebody has already responded to this request, you are not allowed to update it"})
		return
	}

	dto := models.UpdateRequestDTO{}

	if err := c.ShouldBindJSON(&dto); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	if err := r.UpdateById(&dto); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	c.JSON(200, &r)
}

func CancelRequest(c *gin.Context) {
	var r models.Request
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		c.JSON(400, gin.H{"error": "invalid request id"})
		return
	}
	r.ID = uint(id)
	if _, err := r.GetFirst(); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "id not found"})
		return
	}
	uid := c.MustGet("user_id").(uint)
	if r.UserID != uid {
		c.JSON(400, gin.H{"error": "you are not allowed to cancel this request"})
		return
	}

	if r.Status == models.Completed || r.Status == models.RequestCancelled || r.Status == models.Expired {
		c.JSON(400, gin.H{"error": "you are not allowed to cancel it in current state"})
		return
	}

	if err := r.Cancel(); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	c.JSON(200, &r)
}

func AcceptResponse(c *gin.Context) {
	// /accept/:responseId
	respID, err := strconv.Atoi(c.Param("responseId"))
	if err != nil {
		c.JSON(400, gin.H{"error": "invalid response id"})
		return
	}

	var resp models.Response
	var req models.Request

	resp.ID = uint(respID)
	if _, err := resp.GetFirst(); err != nil {
		c.JSON(400, gin.H{"error": "response not found"})
		return
	}

	req.ID = resp.RequestID
	if _, err := req.GetFirst(); err != nil {
		c.JSON(400, gin.H{"error": "request not found"})
		return
	}

	uid := c.MustGet("user_id").(uint)
	if req.UserID != uid {
		c.JSON(http.StatusForbidden, gin.H{"error": "you are not allowed to accept this response"})
		return
	}

	if req.Status != models.WaitingForResponse {
		c.JSON(400, gin.H{"error": "request is not waiting for response"})
		return
	}

	if resp.Status != models.Pending {
		c.JSON(400, gin.H{"error": "response is not pending"})
		return
	}

	if err := resp.Accept(); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}

	c.JSON(200, gin.H{"message": "response accepted"})
}
